import {
  FlatTreeType,
  MenuTreeDataItemType,
  MenuTreeDataType,
  MenuTreeNodeType,
  MenuTreeValueType,
  _MenuTreeValueType,
  _MenuTreeValueItemType,
} from "../type";

// 判断节点是否是叶子节点
export function hasChildren(
  node: MenuTreeDataItemType
): node is MenuTreeNodeType {
  return "children" in node;
}

// 获取树的所有叶子节点
export function getLeafNodes(tree: MenuTreeDataType) {
  const nodes: Array<string | number> = [];
  // 叶子节点对应的操作项目的keys
  let record: Record<string | number, Array<string | number>> = {};

  tree.forEach((node) => {
    if (hasChildren(node)) {
      const r = getLeafNodes(node.children);
      nodes.push(...r.nodes);
      record = { ...Object.assign(record, r.record) };
    } else {
      let operatorsKeys = node.operators?.map((x) => x.value);
      nodes.push(node.key);
      record = {
        ...Object.assign(record, {
          [node.key]: operatorsKeys,
        }),
      };
    }
  });

  return { nodes, record };
}

interface FlattenProps {
  level?: number;
  tree: MenuTreeDataType;
  parentKey?: string | number;
}
export function flatten({
  level = 1,
  tree,
  parentKey = "_root",
}: FlattenProps) {
  const nodes: FlatTreeType = [];
  for (let i = 0; i < tree.length; i++) {
    const item = tree[i];
    const { key, title, ...rest } = item;
    nodes.push({ key, level, title, parentKey, ...rest });
    hasChildren(item) &&
      nodes.push(
        ...flatten({
          tree: item.children,
          level: level + 1,
          parentKey: item.key,
        })
      );
  }
  return nodes;
}

// 获取树的深度
export function getTreeDepth(tree: MenuTreeDataType) {
  let deepth = 0;
  tree.forEach((item) => {
    deepth = hasChildren(item)
      ? Math.max(deepth, getTreeDepth(item.children) + 1)
      : Math.max(deepth, 1);
  });
  return deepth;
}

// 根据部分叶子节点的状态获取整棵树所有节点的状态
export function getAllTreeNodes(
  leafs: _MenuTreeValueType,
  fltaTree: FlatTreeType
): { all: _MenuTreeValueType; checked: MenuTreeValueType } {
  const _v: Record<string | number, _MenuTreeValueItemType> = {};
  leafs.forEach((x) => (_v[x.key] = x));
  const state: Record<string | number, [boolean, boolean]> = {};
  const nodes = fltaTree.sort((m, n) => n.level - m.level);
  const all = nodes.map((node) => {
    const { key, title, parentKey } = node;
    let isChecked: boolean, isAll: boolean;

    if (hasChildren(node)) {
      const { children } = node;
      isChecked = !!children.find(({ key }) => state[key][0]);
      // 判断父节点状态要过滤掉子节点中的禁用状态的节点
      isAll = children
        .filter((x) => !x.disabled)
        .every(({ key }) => state[key][1]);
    } else {
      if (_v[key]) {
        isChecked = _v[key].isChecked;
        isAll = _v[key].isAll;
      } else isChecked = isAll = false;
    }

    state[key] = [isChecked, isAll];

    const obj: _MenuTreeValueItemType = {
      key,
      isChecked,
      isAll,
      title,
      parentKey,
    };

    if (_v[key]?.checkedOperators)
      obj["checkedOperators"] = _v[key].checkedOperators;

    return obj;
  });

  const checked = all
    .filter((x) => x.isChecked)
    .map(({ key, title, parentKey, checkedOperators }) => ({
      key,
      title,
      parentKey,
      checkedOperators,
    }));

  return { all, checked };
}

// 判断两个数组值相等
export function isEqual(
  arr1: Array<string | number>,
  arr2: Array<string | number>
): boolean {
  return (
    arr1.length === arr2.length && !arr1.filter((x) => !arr2.includes(x)).length
  );
}

// 获取一个带操作项的叶子节点的状态
export function getLeafNodeState(
  checkedOperators: Array<string | number> = [],
  operatorsKeys: Array<string | number> = [],
  multiple: boolean = false
): {
  isChecked: boolean;
  isAll: boolean;
} {
  let isChecked: boolean, isAll: boolean;
  const real = checkedOperators.filter((x) => operatorsKeys.includes(x));
  const len = real.length;
  if (multiple) {
    isChecked = !!len;
    isAll = isEqual(operatorsKeys, real);
  } else {
    isAll = isChecked = !!len;
  }
  return { isChecked, isAll };
}

// 根据 key 替换掉原数组的值
export function replaceNodeValue(
  preValue: _MenuTreeValueType,
  array: _MenuTreeValueType
): _MenuTreeValueType {
  const newVal: _MenuTreeValueType = JSON.parse(JSON.stringify(preValue));
  let index = -1;
  array.forEach((x) => {
    index = newVal.findIndex((y) => y.key === x.key);
    if (index > -1) newVal.splice(index, 1, x);
    else newVal.push(x);
  });

  return newVal;
}
